package com.explorex.infra.puma.gateway.netty.redis;

import com.alibaba.fastjson.JSON;
import com.explorex.infra.puma.gateway.netty.common.*;
import com.explorex.infra.puma.gateway.netty.common.utils.JacksonObjectMapper;
import com.explorex.infra.puma.gateway.netty.ws.consts.ChannelConst;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import redis.clients.jedis.Jedis;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;

/**
 * @author: JacceYang
 * @createTime: 2022/09/20 17:51
 * @description: 用户位置信息相关redis 缓存
 */
@Component
@Slf4j
public class BaseSessionRedis {
    private static final int THREE_HUNDRED = 300;
    private static final ExecutorService threadPoolExecutor = Executors.newCachedThreadPool();
    private final static String HEARTBEAT_KEY = "octopus:server.heartbeat";


    private static final ScheduledExecutorService scheduledExecutor = new ScheduledThreadPoolExecutor(1, new BasicThreadFactory.Builder().namingPattern(BaseSessionRedis.class.getSimpleName() + "-%d").build());

    @Autowired(required = false)
    private Jedis sessionRedis;

    public boolean addSessionInfo(BaseSocketSession baseSession, String currentUid) {
        if (baseSession.getAuth() != AuthStatusEnum.AUTHED) {
            return false;
        }
        if (StringUtils.isBlank(currentUid)) {
            log.warn("addSessionInfo with empty currentUid ={}", currentUid);
            return false;
        }


        if (!baseSession.channel().isActive()) {
            return false;
        }

        if (baseSession.getAuthUser() != null) {
            return true;
        }

        String connectorInfo = buildSessionInfoStr(baseSession);
        String locationKey = generateChannelRedisKey(currentUid, baseSession.domain());
        String channelField = generateRedisMapKey(baseSession.channelTag(), baseSession.terminal(), baseSession.channel().id().asLongText());
        sessionRedis.hset(locationKey, channelField, connectorInfo);
        sessionRedis.expire(locationKey, 300);
        if (!baseSession.channel().isActive()) {
            log.info("addSessionInfo channelInactive,delete key:{},mapKey:{}", locationKey, channelField);
            sessionRedis.hdel(locationKey, channelField);
            return false;
        }
        return true;
    }



    public boolean removeSessionInfo(BaseSocketSession baseSocketSession) {
        // channel 就没有bind 过用户的情况，直接返回
        if (baseSocketSession == null) {
            return true;
        }
        UserKey userKey = baseSocketSession.channel().attr(ChannelConst.USER_KEY).get();
        if (userKey == null) {
            return true;
        }


        String userId = generateChannelRedisKey(userKey.getUserId(), userKey.getDomain());
        // 删除所有的bind 的user信息
        String channelField = generateRedisMapKey(baseSocketSession.channelTag(), baseSocketSession.terminal(), baseSocketSession.channel().id().asLongText());
        sessionRedis.hdel(userId, channelField);
        return true;
    }

    public void removeByLocationKeyAndChannelId(String domain,
                                                String userId,
                                                String channelId,
                                                int tag,
                                                int terminal) {
        if (StringUtils.isAnyBlank(domain, userId, channelId)) {
            log.error("removeByLocationKeyAndChannelId lack param,domain:{}, userId:{},channelId:{}", domain, userId, channelId);
            return;
        }

        try {
            String locationKey = generateChannelRedisKey(userId, domain);
            String channelField =generateRedisMapKey(tag, terminal, channelId);
            sessionRedis.hdel(locationKey, channelField);
        } catch (Exception e) {
            log.error("removeByLocationKeyAndChannelId domain:{},userId:{},channelId:{}");
        }
    }


    public void updateSessionInfo(BaseSocketSession baseSession) {
        threadPoolExecutor.execute(() -> {
            // 先更新绑定的
            if (!CollectionUtils.isEmpty(baseSession.getTokenUserInfo())) {
                baseSession.getTokenUserInfo().values().forEach(currentUid -> {
                    updateUserCtxInRedis(baseSession, currentUid);
                });
            }
            // 再更新自身的
            if (baseSession.getAuthUser() != null && StringUtils.isNotBlank(baseSession.getAuthUser().getUserId())) {
                updateUserCtxInRedis(baseSession, baseSession.getAuthUser());
            }
        });
    }




    public void removeVirtualLinkUserCtxInRedis(BaseSocketSession session, UserKey virtualLinkUserInfo) {
        removeUserCtxInRedis(session, virtualLinkUserInfo);
    }

    public void updateVirtualLinkUserCtxInRedis(BaseSocketSession session, UserKey virtualLinkUserInfo) {
        updateUserCtxInRedis(session, virtualLinkUserInfo);
    }

    public void removeUserCtxInRedis(BaseSocketSession session, UserKey userInfo) {

        String locationKey = generateChannelRedisKey(userInfo.getUserId(), userInfo.getDomain());
        String channelField = generateRedisMapKey(session.channelTag(), userInfo.getTerminal(), session.channel().id().asLongText());
        try {
            sessionRedis.hdel(locationKey, channelField);
        } catch (Exception ex) {
            log.error("{} removeUserCtxInRedis failed, userInfo:{}", session, userInfo, ex);
        }
    }

    private String generateChannelRedisKey(String userId, String domain){

        return StringUtils.EMPTY;
    }


    private void updateUserCtxInRedis(BaseSocketSession session, UserKey userInfo) {

        String connectorInfo = buildSessionInfoStr(session);
        String locationKey = generateChannelRedisKey(userInfo.getUserId(), userInfo.getDomain());
        String channelField = generateRedisMapKey(session.channelTag(), userInfo.getTerminal(), session.channel().id().asLongText());
        if (!session.channel().isActive()) {
            return;
        }
        sessionRedis.hset(locationKey, channelField, connectorInfo);
        sessionRedis.expire(locationKey, THREE_HUNDRED);
    }

    private String generateRedisMapKey(int channelTag, int terminal, String asLongText) {

        return StringUtils.EMPTY;
    }

    private String buildSessionInfoStr(BaseSocketSession session) {
        UserConnectionInfoModel model = new UserConnectionInfoModel();

        model.setHost(session.localIp());
        model.setMills(System.currentTimeMillis());

        DeviceBase deviceInfo = session.device();
        model.setPlatform(deviceInfo.getPlatform());
        model.setDeviceId(deviceInfo.getDeviceId());
        model.setOs(deviceInfo.getOs());
        model.setProtocol(session.protocol().ordinal());
        model.setUserStatus(session.userStatus());
        model.setAppVersion(deviceInfo.getAppVersion());
        return JSON.toJSONString(model);
    }


}
